package java_thinking.unit_18_IO.exercise;

import java.io.*;
import java.security.SecureRandom;
import java.util.*;

/**
 * @Desc:   海量日志中提取访问次数前100的IP
 * @author: cww
 * @DateTime: 2020-03-28 23:45
 */

public class TopIpCountFromBigLog {
    private static final String PRE_DIR = "src/maindemo/java/nio/exercise/split/";
    // 保存每个文件的流对象
    public static final Map<Integer, BufferedWriter> bwMap = new HashMap<>();
    //分割文件用，存储若干数量之后再存入某个文件
    public static final Map<Integer, List<String>> dataMap = new HashMap<>();
    // 存储访问次数前100 的 IP
    public static Set<IP> set = new TreeSet<>();
    public static void main(String[] args) throws IOException {
        String file = "src/maindemo/java/java_thinking/unit_18_IO/exercise/LogFile.txt";
        createLog(new File(file), 100000000L);
        splitLog(new File(file), 1000);
        File files = new File(PRE_DIR);
        for(File file1:files.listFiles()){
            statistics(file1);
        }
        for(IP ip:set) {
            System.out.println(String.format("ip:%s nums:%s", ip.getIp(), ip.getNums()));
        }

    }

    /**
     * 生成日志文件
     *
     * @param logFile
     * @param logNums
     */
    public static void createLog(File logFile, long logNums) throws IOException {
        long step1 = System.currentTimeMillis();
        FileWriter fWriter = new FileWriter(logFile, true);
        BufferedWriter bfWriter = new BufferedWriter(fWriter);
        PrintWriter pWrite = new PrintWriter(bfWriter);
        SecureRandom random = new SecureRandom();

        for(int i=0; i<logNums; i++) {
            pWrite.println("192." + random.nextInt(255)+"."+random.nextInt(255)+"."+random.nextInt(255));
            if((i+1)%1000 == 0) {
                pWrite.flush();
            }
        }
        pWrite.flush();
        fWriter.close();
        bfWriter.close();
        pWrite.close();
        long step2 = System.currentTimeMillis();
        System.out.println("total time-consuming: " + (step2 - step1) + " ms");
    }

    /**
     * 将文件进行分割
     * 缺点，一行一行读效率低
     * @param logFile   被分割的文件
     * @param fileNums  分割为几份
     * @throws IOException
     */
    public static void splitLog(File logFile, int fileNums) throws IOException {
        BufferedReader bfReader = new BufferedReader(new FileReader(logFile));
        String ip = bfReader.readLine();
        // 创建文件，以及流对象
        for(int i=0;i<fileNums;i++) {
            File file = new File(PRE_DIR + i + ".txt");
            bwMap.put(i, new BufferedWriter(new FileWriter(file)));
            dataMap.put(i, new LinkedList<>());
        }
        // 这个 while 循环是先把那些达到 list.size == 1000 的写入文件，当while结束后，
        // 必定有些list里面还有剩余的内容还没写入，也未进行 clear，交由下一个 for 执行
        while(ip != null) {
            int slot = Math.abs(ip.hashCode()) % fileNums;
            List<String> list = dataMap.get(slot);
            list.add(ip + "\n");
            if(list.size() % 1000 == 0) {
                BufferedWriter writer = bwMap.get(slot);
                for(String line:list) {
                    writer.write(line);
                }
                writer.flush();
                list.clear();
            }
            ip = bfReader.readLine();
        }
        // 这个for 是将上一个 while 中 list 内部剩余的ip写入文件，而每个文件内部ip数量只能近似相同，
        // 不可能全部等分，因为 hashCode 肯定会有重复
        for(int fn: bwMap.keySet()) {
            List<String> list = dataMap.get(fn);
            BufferedWriter writer = bwMap.get(fn);
            for(String line : list) {
                writer.write(line);
            }
            list.clear();
            writer.flush();
            writer.close();
        }
        bwMap.clear();
        bfReader.close();
    }

    /**
     * 统计分析每个文件中的前100个出现频率最高的 IP
     * 缺点：效率低
     * @param file
     * @throws IOException
     */
    public static void statistics(File file) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(file));
        // 当前文件构建的 TreeSet
        Set<IP> currentSet = new TreeSet<IP>();
        // 遍历每一行，将 IP 放入TreeSet
        String line = reader.readLine();
        while(line != null) {
            String ip = line.trim();
            currentSet.add(new IP(ip, 1));
            line = reader.readLine();
        }
        reader.close();
        // 将当前文件中的前100个IP加入全局 TreeSet，后面再对全局的 TreeSet 进行取前100
        int i = 0;
        for(IP ip:currentSet) {
            set.add(ip);
            if(i>100) {
                break;
            }
            i++;
        }
        // 因为上一步将当前文件的ip加入了全局的 TreeSet，这里需要重新选出全局的前100个
        Set<IP> tmp2 = new TreeSet<IP>();
        int j = 0;
        for(IP ip: set) {
            tmp2.add(ip);
            if(j>100){
                break;
            }
            j++;
        }
        // 更新全局 TreeSet；
        set = tmp2;
        currentSet = null;
        tmp2 = null;
    }
}
